/* 
 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2005-2010, Anthony Minessale II <anthm@freeswitch.org>
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 *
 * The Initial Developer of the Original Code is
 * Anthony Minessale II <anthm@freeswitch.org>
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * 
 * Eduardo Nunes Pereira <eduardonunesp@gmail.com>
 *
 *
 * mod_twitter.c -- Stay connected
 * 
 */
#include <switch.h>
#include <curl/curl.h>

/* Prototypes */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_twitter_shutdown);
SWITCH_MODULE_RUNTIME_FUNCTION(mod_twitter_runtime);
SWITCH_MODULE_LOAD_FUNCTION(mod_twitter_load);

/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime) 
 * Defines a switch_loadable_module_function_table_t and a static const char[] modname
 */
SWITCH_MODULE_DEFINITION(mod_twitter, mod_twitter_load, mod_twitter_shutdown, NULL);

/* The Syntax  */
static char *SYNTAX = "twitter [update|new_status <text>]";

//static char *MOD_TWITTER_USERID = "?user_id=";
#if 0    
    /* Constants */
    const int MOD_TWITTER_DEFAULT_BUFFSIZE = 1024;
    static char *MOD_TWITTER_COLON = ":";
    const char MOD_TWITTER_EOS = '\0';

    /* Miscellaneous data used to build twitter URLs*/
    static char *MOD_TWITTER_REQUESTTOKEN = "request_token";
    static char *MOD_TWITTER_STATUSSTRING = "status=";
    static char *MOD_TWITTER_TEXTSTRING = "text=";
    static char *MOD_TWITTER_QUERYSTRING = "query=";  
    static char *MOD_TWITTER_SEARCHQUERYSTRING = "?q=";      
    static char *MOD_TWITTER_SCREENNAME = "?screen_name=";
    static char *MOD_TWITTER_USERID = "?user_id=";
    static char *MOD_TWITTER_EXTENSIONFORMAT = ".xml";
    static char *MOD_TWITTER_TARGETSCREENNAME = "?target_screen_name=";
    static char *MOD_TWITTER_TARGETUSERID = "?target_id=";
#endif    

/* Status URLs */
static char *MOD_TWITTER_STATUSUPDATE_URL = "http://twitter.com/statuses/update.xml";
//static char *MOD_TWITTER_STATUSSHOW_URL = "http://twitter.com/statuses/show/";
//static char *MOD_TWITTER_STATUDESTROY_URL = "http://twitter.com/statuses/destroy/";


//static char *MOD_TWITTER_FRIENDS_TIMELINE_URL = "http://twitter.com/statuses/friends_timeline.xml";
#if 0

/* Search URLs */
static char *MOD_TWITTER_SEARCH_URL = "http://search.twitter.com/search.atom";

/* Timeline URLs */
static char *MOD_TWITTER_PUBLIC_TIMELINE_URL = "http://twitter.com/statuses/public_timeline.xml";
static char *MOD_TWITTER_FEATURED_USERS_URL = "http://twitter.com/statuses/featured.xml";
static char *MOD_TWITTER_FRIENDS_TIMELINE_URL = "http://twitter.com/statuses/friends_timeline.xml";
static char *MOD_TWITTER_MENTIONS_URL = "http://twitter.com/statuses/mentions.xml";
static char *MOD_TWITTER_USERTIMELINE_URL = "http://twitter.com/statuses/user_timeline.xml";

/* Users URLs */
static char *MOD_TWITTER_SHOWUSERS_URL = "http://twitter.com/users/show.xml";
static char *MOD_TWITTER_SHOWFRIENDS_URL = "http://twitter.com/statuses/friends.xml";
static char *MOD_TWITTER_SHOWFOLLOWERS_URL = "http://twitter.com/statuses/followers.xml";

/* Direct messages URLs */
static char *MOD_TWITTER_DIRECTMESSAGES_URL = "http://twitter.com/direct_messages.xml";
static char *MOD_TWITTER_DIRECTMESSAGENEW_URL = "http://twitter.com/direct_messages/new.xml";
static char *MOD_TWITTER_DIRECTMESSAGESSENT_URL = "http://twitter.com/direct_messages/sent.xml";
static char *MOD_TWITTER_DIRECTMESSAGEDESTROY_URL = "http://twitter.com/direct_messages/destroy/";

/* Friendships URLs */
static char *MOD_TWITTER_FRIENDSHIPSCREATE_URL = "http://twitter.com/friendships/create.xml";
static char *MOD_TWITTER_FRIENDSHIPSDESTROY_URL = "http://twitter.com/friendships/destroy.xml";
static char *MOD_TWITTER_FRIENDSHIPSSHOW_URL = "http://twitter.com/friendships/show.xml";

/* Social graphs URLs */
static char *MOD_TWITTER_FRIENDSIDS_URL = "http://twitter.com/friends/ids.xml";
static char *MOD_TWITTER_FOLLOWERSIDS_URL = "http://twitter.com/followers/ids.xml";

/* Account URLs */
static char *MOD_TWITTER_ACCOUNTRATELIMIT_URL = "http://twitter.com/account/rate_limit_status.xml";

/* Favorites URLs */
static char *MOD_TWITTER_FAVORITESGET_URL = "http://twitter.com/favorites.xml";
static char *MOD_TWITTER_FAVORITECREATE_URL = "http://twitter.com/favorites/create/";
static char *MOD_TWITTER_FAVORITEDESTROY_URL = "http://twitter.com/favorites/destroy/";

/* Block URLs */
static char *MOD_TWITTER_BLOCKSCREATE_URL = "http://twitter.com/blocks/create/";
static char *MOD_TWITTER_BLOCKSDESTROY_URL = "http://twitter.com/blocks/destroy/";

/* Saved Search URLs */
static char *MOD_TWITTER_SAVEDSEARCHGET_URL = "http://twitter.com/saved_searches.xml";
static char *MOD_TWITTER_SAVEDSEARCHSHOW_URL = "http://twitter.com/saved_searches/show/";
static char *MOD_TWITTER_SAVEDSEARCHCREATE_URL = "http://twitter.com/saved_searches/create.xml";
static char *MOD_TWITTER_SAVEDSEARCHDESTROY_URL = "http://twitter.com/saved_searches/destroy/";

#endif

static struct {
    switch_memory_pool_t *pool;
    char *user;
    char *pass;
} globals;

typedef struct http_data_obj {
    switch_stream_handle_t stream;
    switch_size_t bytes;
    switch_size_t max_bytes;
    switch_memory_pool_t *pool;
    int err;
    long http_response_code;
    char *http_response;
    struct curl_slist *headers;
} http_data_t;

static size_t file_callback(void *ptr, size_t size, size_t nmemb, void *data)
{
    register unsigned int realsize = (unsigned int) (size * nmemb);
    http_data_t *http_data = data;

    http_data->bytes += realsize;

    if (http_data->bytes > http_data->max_bytes) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Oversized file detected [%d bytes]\n", (int) http_data->bytes);
        http_data->err = 1;
        return 0;
    }   

    http_data->stream.write_function(&http_data->stream, "%.*s", realsize, ptr);
    return realsize;
}

static size_t header_callback(void *ptr, size_t size, size_t nmemb, void *data)
{
    register unsigned int realsize = (unsigned int) (size * nmemb);
    http_data_t *http_data = data;
    char *header = NULL;

    header = switch_core_alloc(http_data->pool, realsize + 1); 
    switch_copy_string(header, ptr, realsize);
    header[realsize] = '\0';

    http_data->headers = curl_slist_append(http_data->headers, header);

    return realsize;
}

static http_data_t *do_lookup_url(switch_memory_pool_t *pool, const char *url, const char *method, const char *data)
{
    CURL *curl_handle = NULL;
    long httpRes = 0;
    char hostname[256] = ""; 
    int user_pass_size = strlen(globals.user) + strlen(globals.pass) + 2;
    char user_pass[user_pass_size];

    http_data_t *http_data = NULL;

    http_data = switch_core_alloc(pool, sizeof(http_data_t));
    memset(http_data, 0, sizeof(http_data_t));
    http_data->pool = pool;

    http_data->max_bytes = 64000;
    SWITCH_STANDARD_STREAM(http_data->stream);

    gethostname(hostname, sizeof(hostname));

    if (!method) {
        method = "get";
    }

    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "method: %s, url: %s\n", method, url);
    curl_handle = curl_easy_init();

    switch_snprintf(user_pass,user_pass_size,"%s:%s",globals.user, globals.pass);
    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Twitter user:pass [%s]\n", user_pass);

	if (!strncasecmp(url, "https", 5)) {
		curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
		curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
	}
	if (!strcasecmp(method, "head")) {
		curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1);
	} else if (!strcasecmp(method, "post")) {
		curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, strlen(data));
		curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, (void *) data);
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Post data: %s\n", data);
	} else {
		curl_easy_setopt(curl_handle, CURLOPT_HTTPGET, 1);
	}

    curl_easy_setopt(curl_handle, CURLOPT_USERPWD, user_pass);
    curl_easy_setopt(curl_handle, CURLOPT_URL, url);
    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, file_callback);
    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) http_data);
    curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback);
    curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *) http_data);
    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-curl/1.0");

    curl_easy_perform(curl_handle);
    curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes);
    curl_easy_cleanup(curl_handle);

    if (http_data->stream.data && !zstr((char *) http_data->stream.data) && strcmp(" ", http_data->stream.data)) {
        http_data->http_response = switch_core_strdup(pool, http_data->stream.data);
    }   

    http_data->http_response_code = httpRes;

    switch_safe_free(http_data->stream.data);
    return http_data;
}

static switch_xml_config_item_t instructions[] = { 
    SWITCH_CONFIG_ITEM("user",                 /* key  */ 
                        SWITCH_CONFIG_STRING,  /* type */
                        CONFIG_RELOADABLE,     /* flag */
                        &globals.user,         /* pointer */
                        "",                    /* default value */
                        NULL,                  /* data */ 
                        NULL,                  /* syntax */
                        NULL),                 /* helper */ 
   
    SWITCH_CONFIG_ITEM("pass",                 /* key  */ 
                        SWITCH_CONFIG_STRING,  /* type */
                        CONFIG_RELOADABLE,     /* flag */
                        &globals.pass,         /* pointer */
                        "",                    /* default value */
                        NULL,                  /* data */ 
                        NULL,                  /* syntax */
                        NULL),                 /* helper */ 

    SWITCH_CONFIG_ITEM_END()
};

static switch_status_t read_configs()
{
    switch_xml_t xml, x_lists, x_list, cfg;

    if ((xml = switch_xml_open_cfg("twitter.conf", &cfg, NULL))) {
        if ((x_lists = switch_xml_child(cfg, "settings"))) {
            for (x_list = switch_xml_child(x_lists, "param"); x_list; x_list = x_list->next) {
                const char *name = switch_xml_attr(x_list, "name"); 
                const char *value = switch_xml_attr(x_list, "value");

                /* Ignore empty/missing attributes */
                if (switch_strlen_zero(name))  continue;
                if (switch_strlen_zero(value)) continue;

                if (!strcmp(name, "user")) {
                    globals.user = (char*) value;
                } else if (!strcmp(name, "pass")) {
                    globals.pass = (char*) value;
                } else {
                    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown attribute %s\n", name);
                }
            }
        }
    } else {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load mod_twitter config!\n");
        return SWITCH_STATUS_FALSE;
    }

    return SWITCH_STATUS_SUCCESS;
}

static switch_status_t do_config(switch_bool_t reload)
{
    memset(&globals, 0, sizeof(globals));

    if (switch_xml_config_parse_module_settings("twitter.conf", reload, instructions) != SWITCH_STATUS_SUCCESS) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not open twitter.conf\n");
        return SWITCH_STATUS_FALSE;
    }   

    return read_configs();
}

SWITCH_STANDARD_API(twitter_function)
{
    switch_status_t status = SWITCH_STATUS_SUCCESS;
    switch_memory_pool_t *pool = NULL;
    char *data = NULL;
    int argc;
    char *argv[10] = { 0 };
    char *url = NULL;

    http_data_t *http_data = NULL;

    if (zstr(cmd)) {
        switch_goto_status(SWITCH_STATUS_SUCCESS, usage);
    }

    if (zstr(globals.user) || zstr(globals.pass)) {
        stream->write_function(stream,"user or pass, cannot be blank");
        switch_goto_status(SWITCH_STATUS_SUCCESS, usage);
    }

    if (session) {
        pool = switch_core_session_get_pool(session);
    } else {
        switch_core_new_memory_pool(&pool);
    }

    data = strdup(cmd);
    if ((argc = switch_separate_string(data, ' ',argv, (sizeof(argv) / sizeof(argv[0]))))) {
        if (argc < 1) {
            switch_goto_status(SWITCH_STATUS_SUCCESS, usage);
        }

        if (!strcasecmp("update", argv[0])) {
            stream->write_function(stream, "update");
            
            url = MOD_TWITTER_STATUSUPDATE_URL;
            http_data = do_lookup_url(pool,url,"get","");
            
            if(http_data)
            {
                stream->write_function(stream,"url %s\n",url);
                stream->write_function(stream,http_data->http_response);
            }
            
            switch_goto_status(SWITCH_STATUS_SUCCESS, done);
        } else if (!strcasecmp("new_status", argv[0])) {

            stream->write_function(stream, do_lookup_url(pool,"www.google.com.br","get","")->http_response);
            switch_goto_status(SWITCH_STATUS_SUCCESS, done);
        } else if (!strcasecmp("confs", argv[0])) {
            stream->write_function(stream,"user: %s\n",globals.user);
            stream->write_function(stream,"pass: %s\n",globals.pass);
            switch_goto_status(SWITCH_STATUS_SUCCESS, done);
        }
    }

    usage:
        stream->write_function(stream, "-ERR\n%s\n", SYNTAX);
        switch_goto_status(status,done);
    done:
        if (!session && pool) {
            switch_core_destroy_memory_pool(&pool);
        }

    return status;
}

SWITCH_STANDARD_APP(twitter_app_function)
{
    return;
}

SWITCH_MODULE_LOAD_FUNCTION(mod_twitter_load)
{
    switch_api_interface_t * api_interface;
    switch_application_interface_t * app_interface;

    /* connect my internal structure to the blank pointer passed to me */
	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
	
    memset(&globals, 0, sizeof(globals));
    
    globals.pool = pool;

    do_config(SWITCH_FALSE);

    SWITCH_ADD_API(api_interface, "twitter", "Twitter api", twitter_function, SYNTAX);
    switch_console_set_complete("add twitter update");
    switch_console_set_complete("add twitter new_status");
    switch_console_set_complete("add twitter confs");

    SWITCH_ADD_APP(app_interface, "twitter", "Twitter app", "Twitter functions", twitter_app_function, SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);   

	/* indicate that the module should continue to be loaded */
	return SWITCH_STATUS_SUCCESS;
}

/*
  Called when the system shuts down
  Macro expands to: switch_status_t mod_twitter_shutdown() */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_twitter_shutdown)
{
	/* Cleanup dynamically allocated config settings */
	switch_xml_config_cleanup(instructions);
	return SWITCH_STATUS_SUCCESS;
}

/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4
 */
